////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek, 1999-2010.
// -------------------------------------------------------------------------
//  File name:	AssetTextureItem.cpp
//  Version:	v1.00
//  Created:	15/04/2009 by Paulo Zaffari
//  Description:	Implementation of AssetTextureItem.h
// -------------------------------------------------------------------------  
//  History:
//		15/04/2009	11:00 - Paulo Zaffari - created
//		12/03/2010	17:30 - Nicusor Nedelcu - refactored
//		10/05/2010	13:49 - Nicusor Nedelcu - stripped common code to AssetBrowserCommon.h/cpp
//
//////////////////////////////////////////////////////////////////////////// 

#include "StdAfx.h"
#include "AssetTextureItem.h"
#include "Util/MemoryBlock.h"
#include "Util/Image.h"
#include "Util/ImageUtil.h"
#include "Util/PathUtil.h"
#include "Include/IAssetDisplayDatabase.h"
#include "ITexture.h"
#include "IRenderer.h"
#include "Include/IAssetViewer.h"
#include "ImageExtensionHelper.h"
#include "Controls/AssetViewer.h"

namespace AssetBrowser
{
	const float kTexturePreviewZoomSpeedDamper = 2000.0f;
	const int		kMouseWheelDeltaStep = ( WHEEL_DELTA == 0 ? 1 : WHEEL_DELTA );
	const float kTexturePreviewMinZoom = 0.01f;
	const float kTexturePreviewMaxZoom = 20.0f;
	const float kTexturePreviewGridScaleBackColor = 0.7f;
	const int		kTexturePreviewInfoTextMargin = 5;
};

CAssetBrowserPreviewTextureDlg	CAssetTextureItem::s_texturePreviewDlg;

CAssetTextureItem::CAssetTextureItem(): 
CAssetDisplay(),
m_strSurfaceTypeString(""),
m_bHasAlphaChannel(false),
m_bIsCubemap(false),
m_nTextureWidth(0),
m_nTextureHeight(0),
m_nMips(0),
m_format(eTF_Unknown),
m_previewDrawingMode(eAssetTextureDrawing_RGB),
m_previewRenderZoom(1.0f),
m_bPreviewSmooth(true),
m_previewMipLevel(0),
m_previewBackColor(RGB( 100, 100, 100 ))
{
	m_flags |= eAssetFlags_UseGdiRendering | eAssetFlags_InteractiveRenderSupported | eAssetFlags_ThreadCachingSupported | eAssetFlags_ThreadFieldsInfoCachingSupported;
}

CAssetTextureItem::~CAssetTextureItem()
{
	// empty, call FreeData first
}

HRESULT STDMETHODCALLTYPE CAssetTextureItem::QueryInterface( const IID &riid, void **ppvObj ) 
{ 
	if( riid == __uuidof(IAssetDisplay) /* && m_pIntegrator*/ )
	{
		*ppvObj = this;
		return S_OK;
	}
	return E_NOINTERFACE ; 
}

ULONG STDMETHODCALLTYPE CAssetTextureItem::AddRef()
{
	return ++m_ref;
};

ULONG STDMETHODCALLTYPE CAssetTextureItem::Release() 
{ 
	if( (--m_ref) == 0 )
	{
		FreeData();
		delete this;
		return 0; 
	}
	else
		return m_ref;
}

bool CAssetTextureItem::GetAssetFieldValue( const char* pFieldName, void* pDest )
{
	if( IS_ASSET_FIELD_NAME( "thumbToolTipText" ) )
	{
		CString tagsText;
		CAssetDisplay::GetAssetFieldValue( "tags", &tagsText );
		stack_string str;
		str.Format( "Path: %s\nWidth: %d\nHeight: %d\nMips: %d\nFilesize: %.2f kB\nTags: %s\n", m_strRelativePath.GetBuffer(), m_nTextureWidth, m_nTextureHeight, m_nMips, (float) m_nFileSize / 1024.0f, tagsText.GetBuffer() );
		*(CString*)pDest = str.c_str();
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "thumbOneLineText" ) && m_nTextureWidth )
	{
		stack_string str;
		str.Format( "[ %d x %d ]", m_nTextureWidth, m_nTextureHeight );
		*(CString*)pDest = str.c_str();
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "errors" ) )
	{
		CString str;

		if( m_nMips == 1 )
		{
			str += "WARNING: MipCount is zero\n";
		}

		*(CString*)pDest = str.GetBuffer();
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "width" ) )
	{
		*(int*)pDest = m_nTextureWidth;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "height" ) )
	{
		*(int*)pDest = m_nTextureHeight;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "mips" ) )
	{
		*(int*)pDest = m_nMips;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "format" ) )
	{
		*(CString*)pDest = CImageExtensionHelper::NameForTextureFormat(m_format);
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "type" ) )
	{
		*(CString*)pDest = m_bIsCubemap ? "Cube" : "2D";
		return true;
	}

	// else, check if the common fields are requested
	return CAssetDisplay::GetAssetFieldValue( pFieldName, pDest );
}

bool CAssetTextureItem::Cache()
{
	if( m_flags & eAssetFlags_Cached )
	{
		return true;
	}

	if( m_flags & eAssetFlags_ThumbnailCached )
	{
		return true;
	}

	CImage img;
	CAlphaBitmap bmp;

	CString str = m_strRelativePath;
	str += m_strFilename;
	
	if( !CImageUtil::LoadImage( str.GetBuffer(), img ) )
	{
		SetFlag( eAssetFlags_Invalid, true );
		return false;
	}

	img.SwapRedAndBlue();
	unsigned int* pData = img.GetData();

	if( !pData )
	{
		img.Release();
		return false;
	}

	if( 0 == img.GetWidth() || 0 == img.GetHeight() )
	{
		img.Release();
		return false;
	}

	if( !bmp.Create( pData, img.GetWidth(), img.GetHeight(), true, false ) )
	{
		img.Release();
		return false;
	}

	// create and resize the bitmap, so we dont have to stretch when drawing the thumb item
	if( !m_oCachedThumbBmp.Create( NULL, gSettings.sAssetBrowserSettings.nThumbSize, gSettings.sAssetBrowserSettings.nThumbSize ) )
	{
		img.Release();
		return false;
	}

	// compute correct ratio to scale the image right
	float ratioX = 1, ratioY = 1;
	UINT newWidth = img.GetWidth();
	UINT newHeight = img.GetHeight();

	if( newWidth > newHeight )
	{
		if( newWidth > 0 )
		{
			ratioY = (float) newHeight / newWidth;
		}
	}
	else
	{
		if( newHeight > 0 )
		{
			ratioX = (float) newWidth / newHeight;
		}
	}

	newWidth = ratioX * gSettings.sAssetBrowserSettings.nThumbSize;
	newHeight = ratioY * gSettings.sAssetBrowserSettings.nThumbSize;

	if( !( m_flags & eAssetFlags_ThumbnailCached ) )
	{
		m_oCachedThumbBmp.GetDC().SetStretchBltMode( HALFTONE );
		//m_oCachedThumbBmp.GetDC().AlphaBlend( 0, 0, gSettings.sAssetBrowserSettings.nThumbSize, gSettings.sAssetBrowserSettings.nThumbSize,
		//																 &m_oCheckersBmp.GetDC(), 0, 0, m_oCheckersBmp.GetWidth(), m_oCheckersBmp.GetHeight(), blendFunc );
		m_oCachedThumbBmp.GetDC().StretchBlt(	( gSettings.sAssetBrowserSettings.nThumbSize - newWidth ) / 2,
																					( gSettings.sAssetBrowserSettings.nThumbSize - newHeight ) / 2,
																					newWidth, newHeight, &bmp.GetDC(), 0, 0, bmp.GetWidth(), bmp.GetHeight(), SRCCOPY );
	}

	m_nTextureWidth = img.GetWidth();
	m_nTextureHeight = img.GetHeight();
	m_nMips = img.GetNumberOfMipMaps();
	m_format = img.GetFormat();
	m_bIsCubemap = img.IsCubemap();
	m_strDccFilename = img.GetDccFilename();

	if( 0 == m_nMips )
	{
		m_flags |= eAssetFlags_HasErrors;
	}

	SetFlag( eAssetFlags_Cached, true );
	SetFlag( eAssetFlags_ThumbnailCached, true );

	if( (m_flags & eAssetFlags_CachedFieldsInfo) == 0)
	{
		SetFlag( eAssetFlags_CachedFieldsInfo, true );
		GetOwnerDisplayDatabase()->OnMetaDataChange(this);
	}

	if( 0 >= m_nTextureWidth || 0 >= m_nTextureHeight )
	{
		SetFlag( eAssetFlags_Invalid, true );
	}

	// lets push this asset to the thumbs pool/queue, so we can have a pool of thumbs kept in memory until they are too old
	// this help browsing the assets, no need to recache each time a row of assets thumbs previously 
	if( m_piOwnerDatabase && m_piOwnerDatabase->GetAssociatedViewer() )
	{
		m_piOwnerDatabase->GetAssociatedViewer()->PushToThumbsCacheQueue( this );
	}

	bmp.Free();
	img.Release();

	return true;
}

bool CAssetTextureItem::CacheFieldsInfo()
{
	// just load the texture and get its fields, like mip, width, height
	// if not already loaded
	if( m_flags & eAssetFlags_CachedFieldsInfo )
		return true;

	CImage img;
	CString str = m_strRelativePath;

	str += m_strFilename;

	if( !CImageUtil::LoadImage( str.GetBuffer(), img ) )
	{
		SetFlag( eAssetFlags_Invalid, true );
		img.Release();
		return false;
	}

	m_nTextureWidth = img.GetWidth();
	m_nTextureHeight = img.GetHeight();
	m_nMips = img.GetNumberOfMipMaps();
	m_format = img.GetFormat();
	m_bIsCubemap = img.IsCubemap();
	m_strDccFilename = img.GetDccFilename();

	if( 0 == m_nMips )
	{
		m_flags |= eAssetFlags_HasErrors;
	}

	SetFlag( eAssetFlags_CachedFieldsInfo, true );
	GetOwnerDisplayDatabase()->OnMetaDataChange(this);

	if( 0 >= m_nTextureWidth || 0 >= m_nTextureHeight )
	{
		SetFlag( eAssetFlags_Invalid, true );
	}

	img.Release();
	return true;
}

bool CAssetTextureItem::UnCache()
{
	if( !( m_flags & eAssetFlags_Cached ) )
	{
		return true;
	}

	SetFlag( eAssetFlags_Cached, false );

	return true;
}

bool CAssetTextureItem::UnCacheThumbnail()
{
	if( !( m_flags & eAssetFlags_ThumbnailCached ) )
	{
		return true;
	}
	
	SetFlag( eAssetFlags_ThumbnailCached, false );
	m_oCachedThumbBmp.Free();

	return true;
}

void CAssetTextureItem::OnBeginPreview( const HWND hQuickPreviewWnd, const HDC hMemDC )
{
	CImage img;
	CString str = m_strRelativePath;

	str += m_strFilename;

	if( !CImageUtil::LoadImage( str.GetBuffer(), img ) )
	{
		SetFlag( eAssetFlags_Invalid, true );
		return;
	}

	img.SwapRedAndBlue();
	
	unsigned int* pData = img.GetData();

	if( !pData )
	{
		img.Release();
		return;
	}

	if( 0 == img.GetWidth() || 0 == img.GetHeight() )
	{
		img.Release();
		return;
	}

	CAlphaBitmap bmp;

	if( !bmp.Create( pData, img.GetWidth(), img.GetHeight(), true, false ) )
	{
		img.Release();
		return;
	}

	if( !m_oPreviewBmp.Create( pData, img.GetWidth(), img.GetHeight(), true, true ) )
	{
		img.Release();
		return;
	}

	// Create the alpha-only thumbnail.
	BITMAP bmpInfo;

	m_oPreviewBmp.GetBitmap().GetBitmap( &bmpInfo );
	int byteCount = bmpInfo.bmWidth * bmpInfo.bmHeight * ( bmpInfo.bmBitsPixel / 8 );
	std::vector<unsigned char> bits;
	
	bits.resize( byteCount );

	if( bits.empty() )
	{
		img.Release();
		return;
	}
	
	if( !bmp.GetBitmap().GetBitmapBits( byteCount, &bits[0] ) )
	{
		img.Release();
		bmp.Free();
		return;
	}

	for( UINT i = 0; i < byteCount; i += 4 )
	{
		bits[i] = bits[i + 1] = bits[i + 2] = bits[i + 3];
		bits[i + 3] = 255;
	}

	m_oPreviewBmpAlpha.Create( &bits[0], bmpInfo.bmWidth, bmpInfo.bmHeight, true, false );

	// Create the RGB-only thumbnail. (The 'StretchBlt' ignores the alpha channel.)
	if( m_oPreviewBmpRGB.Create(NULL, bmpInfo.bmWidth, bmpInfo.bmHeight, false, false ) )
	{
		m_oPreviewBmpRGB.GetDC().SetStretchBltMode( HALFTONE );
		m_oPreviewBmpRGB.GetDC().StretchBlt( 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &bmp.GetDC(), 0, 0, bmp.GetWidth(), bmp.GetHeight(), SRCCOPY );
	}

	bmp.Free();

	CRect rc;
	::GetClientRect( hQuickPreviewWnd, &rc );
	PreviewCenterAndScaleToFit( rc );

	m_hPreviewDC = hMemDC;
}

void CAssetTextureItem::OnEndPreview()
{
	m_hPreviewDC = 0;
	m_oPreviewBmp.Free();
	m_oPreviewBmpRGB.Free();
	m_oPreviewBmpAlpha.Free();
}

void CAssetTextureItem::PreviewCenterAndScaleToFit( const CRect& rViewport )
{
	float ratio = 1.0f;
	UINT newWidth = m_oPreviewBmp.GetWidth();
	UINT newHeight = m_oPreviewBmp.GetHeight();

	if( newWidth > rViewport.Width() )
	{
		if( newWidth > 0 )
		{	
			ratio = (float) rViewport.Width() / newWidth;
		}

		newWidth = rViewport.Width();
		newHeight *= ratio;
	}

	if( newHeight > rViewport.Height() )
	{
		if( newHeight > 0 )
		{
			ratio = (float) rViewport.Height() / newHeight;
		}

		newHeight = rViewport.Height();
		newWidth *= ratio;
	}

	m_previewSize.SetSize( newWidth, newHeight );
	m_previewOffset.x = rViewport.left + rViewport.Width() / 2 - newWidth / 2;
	m_previewOffset.y = rViewport.top + rViewport.Height() / 2 - newHeight / 2;
	m_previewRenderZoom = 1.0f;
}

void CAssetTextureItem::InteractiveRender(	HWND hRenderWindow, const CRect& rstViewport, int aMouseX, int aMouseY,
																						int aMouseDeltaX, int aMouseDeltaY, int aMouseWheelDelta, UINT aKeyFlags )
{
	float zoomDelta = 0.1f * (float)aMouseWheelDelta / AssetBrowser::kMouseWheelDeltaStep;

	m_previewRenderZoom += zoomDelta;
	m_previewRenderZoom = CLAMP( m_previewRenderZoom, AssetBrowser::kTexturePreviewMinZoom, AssetBrowser::kTexturePreviewMaxZoom );

	if( aMouseWheelDelta < 0 )
	{
		zoomDelta = 1.0f - fabs( zoomDelta );
	}
	else
	{
		zoomDelta = 1.0f + fabs( zoomDelta );
	}

	if( 0 != aMouseWheelDelta )
	{
		float zoomPtx = 0;
		float zoomPty = 0;

		assert( m_previewRenderZoom != 0.0f );
		zoomPtx = ( (float)aMouseX - m_previewOffset.x ) / m_previewRenderZoom;
		zoomPty = ( (float)aMouseY - m_previewOffset.y ) / m_previewRenderZoom;
		m_previewRenderZoom += (float)aMouseWheelDelta / AssetBrowser::kTexturePreviewZoomSpeedDamper;

		CPoint newZoomCenter;
		newZoomCenter.x = (float)( zoomPtx * m_previewRenderZoom + m_previewOffset.x );
		newZoomCenter.y = (float)( zoomPty * m_previewRenderZoom + m_previewOffset.y );

		m_previewOffset.x -= newZoomCenter.x - aMouseX;
		m_previewOffset.y -= newZoomCenter.y - aMouseY;
	}

	if( aKeyFlags & MK_LBUTTON )
	{
		m_previewOffset.x += aMouseDeltaX;
		m_previewOffset.y += aMouseDeltaY;
	}

	Render( hRenderWindow, rstViewport, false );
}

void CAssetTextureItem::OnInteractiveRenderKeyEvent( bool bKeyDown, UINT aChar, UINT aKeyFlags )
{
	// empty
}

bool CAssetTextureItem::Render( HWND hRenderWindow, const CRect& rstViewport, bool bCacheThumbnail )
{
	// if we are in preview mode
	if( m_hPreviewDC )
	{
		CDC dc;

		dc.Attach( m_hPreviewDC );

		CBrush brush;
		CPoint pt;

		brush.CreateHatchBrush( HS_DIAGCROSS, CGdiCanvas::ScaleColor( m_previewBackColor, AssetBrowser::kTexturePreviewGridScaleBackColor ) );
		dc.SetBkColor( m_previewBackColor );
		dc.SetBkMode( OPAQUE );
		dc.FillRect( &rstViewport, &brush );

		int newWidth = m_previewSize.cx*m_previewRenderZoom;
		int newHeight = m_previewSize.cy*m_previewRenderZoom;
		pt.x = m_previewOffset.x;
		pt.y = m_previewOffset.y;

		dc.SetStretchBltMode( m_bPreviewSmooth ? HALFTONE : COLORONCOLOR );

		if( m_previewDrawingMode == eAssetTextureDrawing_RGBA )
		{
			BLENDFUNCTION blendFunc;

			CGdiCanvas::MakeBlendFunc( 0xFF, blendFunc );

			dc.AlphaBlend( pt.x, pt.y, newWidth, newHeight, &m_oPreviewBmp.GetDC(), 0, 0, m_oPreviewBmp.GetWidth(), m_oPreviewBmp.GetHeight(), blendFunc );
		}
		else
		if( m_previewDrawingMode == eAssetTextureDrawing_Alpha )
		{
			dc.StretchBlt( pt.x, pt.y, newWidth, newHeight, &m_oPreviewBmpAlpha.GetDC(), 0, 0, m_oPreviewBmpAlpha.GetWidth(), m_oPreviewBmpAlpha.GetHeight(), SRCCOPY );
		}
		else
		if( m_previewDrawingMode == eAssetTextureDrawing_RGB )
		{
			dc.StretchBlt( pt.x, pt.y, newWidth, newHeight, &m_oPreviewBmpRGB.GetDC(), 0, 0, m_oPreviewBmpRGB.GetWidth(), m_oPreviewBmpRGB.GetHeight(), SRCCOPY );
		}

		CFont fntInfo;
		fntInfo.CreatePointFont( 90, "Arial Bold" );

		dc.SelectObject( fntInfo );
		dc.SetBkMode( TRANSPARENT );

		CString strTmp, strDrawingMode;

		if( m_previewDrawingMode == eAssetTextureDrawing_Alpha )
			strDrawingMode = "ALPHA";

		if( m_previewDrawingMode == eAssetTextureDrawing_RGB )
			strDrawingMode = "RGB";

		if( m_previewDrawingMode == eAssetTextureDrawing_RGBA )
			strDrawingMode = "RGBA";

		strTmp.Format( "%s (%d%%, %dx%d)", strDrawingMode.GetBuffer(), (int)(m_previewRenderZoom*100.0f), m_nTextureWidth, m_nTextureHeight );

		CRect rc = rstViewport;

		rc.DeflateRect( AssetBrowser::kTexturePreviewInfoTextMargin, AssetBrowser::kTexturePreviewInfoTextMargin );

		dc.SetTextColor( RGB( 0, 0, 0 ) );
		rc.OffsetRect( 1, 1 );
		dc.DrawText( strTmp.GetBuffer(), &rc, DT_WORD_ELLIPSIS );

		dc.SetTextColor( RGB( 255, 255, 0 ) );
		rc.OffsetRect( -1, -1 );
		dc.DrawText( strTmp.GetBuffer(), &rc, DT_WORD_ELLIPSIS );

		fntInfo.DeleteObject();

		dc.Detach();
	}
	else
	{
		// normal render of texture thumb
		CDC dc;
		HDC hdc = ::GetDC( hRenderWindow );
		
		dc.Attach( hdc );
		DrawThumbImage( hdc, rstViewport );
		dc.Detach();
		::ReleaseDC( hRenderWindow, hdc );
	}

	return true;
}

bool CAssetTextureItem::DrawThumbImage( HDC hDC, const CRect& rRect )
{
	if( m_flags & eAssetFlags_ThumbnailCached )
	{
		CDC dc;
		CPoint pt;

		dc.Attach( hDC );
		pt.x = rRect.left;
		pt.y = rRect.top;
		dc.BitBlt( pt.x, pt.y, rRect.Width(), rRect.Height(), &m_oCachedThumbBmp.GetDC(), 0, 0, SRCCOPY );
		dc.Detach();

		return true;
	}

	return false;
}

void CAssetTextureItem::CacheFieldsInfoForLoadedTex( const ITexture *pTexture )
{
	if( m_flags & eAssetFlags_CachedFieldsInfo )
		return;

	m_nTextureWidth = pTexture->GetWidth();
	m_nTextureHeight = pTexture->GetHeight();
	m_nMips = pTexture->GetNumMips();
	m_format = pTexture->GetTextureDstFormat();
	m_bIsCubemap = pTexture->GetTextureType() == eTT_Cube; 

	if( 0 == m_nMips )
	{
		m_flags |= eAssetFlags_HasErrors;
	}

	SetFlag( eAssetFlags_CachedFieldsInfo, true );
	GetOwnerDisplayDatabase()->OnMetaDataChange(this);

	if( 0 >= m_nTextureWidth || 0 >= m_nTextureHeight )
	{
		SetFlag( eAssetFlags_Invalid, true );
	}
}

void CAssetTextureItem::DrawTextOnReportImage( CAlphaBitmap &abm ) const
{
	const COLORREF filenameShadowColor = RGB( 0, 0, 0 );
	const COLORREF filenameColor = RGB( 255, 255, 0 );
	const COLORREF otherInfosColor = RGB( 0, 0, 0 );
	CFont fontInfoTitle, fontInfo;
	fontInfoTitle.CreatePointFont(95, "Arial Bold");
	fontInfo.CreatePointFont(80, "Arial");

	CDC dc;
	dc.Attach(abm.GetDC());
	dc.SetBkMode(TRANSPARENT);
	dc.SelectObject( fontInfoTitle );
	dc.SetTextColor(filenameShadowColor);
	dc.TextOut(AssetViewer::kOverlayTextLeftMargin+1, 
						AssetViewer::kOverlayTextTopMargin+1, m_strFilename);
	dc.SetTextColor(filenameColor);
	dc.TextOut(AssetViewer::kOverlayTextLeftMargin, 
						AssetViewer::kOverlayTextTopMargin, m_strFilename);

	CString reportInfos;
	reportInfos.Format( 
		"Path: %s\nRes: %dx%d\nFormat: %s\nType: %s\n",
		m_strRelativePath,
		m_nTextureWidth,
		m_nTextureHeight,
		CImageExtensionHelper::NameForTextureFormat(m_format),
		m_bIsCubemap ? "Cube" : "2D");

	CSize	titleSize;
	titleSize = dc.GetTextExtent(m_strFilename);
	CRect rcTextInfo;
	rcTextInfo.left = AssetViewer::kOverlayTextLeftMargin;
	rcTextInfo.right = abm.GetWidth();
	rcTextInfo.top = titleSize.cy + 15;
	rcTextInfo.bottom = abm.GetHeight();
	dc.SetTextColor(otherInfosColor);
	dc.SelectObject(fontInfo);
	dc.DrawText(reportInfos.GetBuffer(), &rcTextInfo, DT_WORD_ELLIPSIS);
	dc.Detach();

	fontInfoTitle.DeleteObject();
	fontInfo.DeleteObject();
}

bool CAssetTextureItem::SaveReportImage( const char *filePath ) const
{
	if((m_flags & eAssetFlags_ThumbnailCached) == 0)
		return false;

	CAlphaBitmap& abm = const_cast<CAlphaBitmap&>(m_oCachedThumbBmp);
	CBitmap& bitmap = abm.GetBitmap();
	BITMAP bmpInfo;
	bitmap.GetBitmap(&bmpInfo);
	int byteCount = bmpInfo.bmWidth*bmpInfo.bmHeight*(bmpInfo.bmBitsPixel/8);

	bool bImageIsWideEnough = bmpInfo.bmWidth >= AssetViewer::kMinWidthForOverlayText;

	std::vector<unsigned char> bitsBackUp;
	if(bImageIsWideEnough)					// Back up the bitmap and draw the overlay text.
	{
		bitsBackUp.resize(byteCount);
		bitmap.GetBitmapBits(byteCount, &bitsBackUp[0]);
		DrawTextOnReportImage(abm);
	}

	CImage image;
	image.Allocate(bmpInfo.bmWidth, bmpInfo.bmHeight);
	bitmap.GetBitmapBits(byteCount, image.GetData());
	image.SwapRedAndBlue();

	if(bImageIsWideEnough)				// Restore the bitmap.
		bitmap.SetBitmapBits(byteCount, &bitsBackUp[0]);

	return CImageUtil::SaveBitmap(filePath, image);
}

bool CAssetTextureItem::SaveReportText( const char *filePath ) const
{
	FILE *file = fopen( filePath, "wt" );

	if( file == NULL )
		return false;

	fprintf(file,
					"Filename: %s\nPath: %s\nRes: %dx%d\nFormat: %s\nType: %s\n",
					m_strFilename,
					m_strRelativePath,
					m_nTextureWidth,
					m_nTextureHeight,
					CImageExtensionHelper::NameForTextureFormat(m_format),
					m_bIsCubemap ? "Cube" : "2D");

	fclose(file);

	return true;
}

CDialog* CAssetTextureItem::GetCustomPreviewPanel( CWnd* pParentWnd )
{
	s_texturePreviewDlg.m_pTexture = this;

	if( !s_texturePreviewDlg.GetSafeHwnd() )
	{
		if( !s_texturePreviewDlg.Create( CAssetBrowserPreviewTextureDlg::IDD, (CWnd*)pParentWnd ) )
		{
			AfxMessageBox( "Could not create texture preview dialog panel" );
		}
	}
	else
	{
		s_texturePreviewDlg.SetParent( (CWnd*)pParentWnd );
		s_texturePreviewDlg.Init();
	}

	return &s_texturePreviewDlg;
}

void CAssetTextureItem::ToXML(XmlNodeRef& node) const
{
	assert(m_flags & eAssetFlags_CachedFieldsInfo);

	node->setTag("Texture");
	CString fileName = m_strRelativePath + m_strFilename;
	node->setAttr("fileName", fileName.GetBuffer());
	node->setAttr("dccFilename", m_strDccFilename);
	node->setAttr("nTextureWidth", m_nTextureWidth);
	node->setAttr("nTextureHeight", m_nTextureHeight);
	node->setAttr("nMips", m_nMips);
	node->setAttr("format", (int)m_format);
	node->setAttr("bIsCubemap", m_bIsCubemap);
	bool bIsFav = m_flags & eAssetFlags_Favourite;
	node->setAttr("fav", bIsFav);
}

void CAssetTextureItem::FromXML(const XmlNodeRef& node) 
{
	assert((m_flags & eAssetFlags_CachedFieldsInfo) == 0);
	assert(node->isTag("Texture"));

	if(node->isTag("Texture") == false)
		return;

	node->getAttr("nTextureWidth", m_nTextureWidth);
	node->getAttr("nTextureHeight", m_nTextureHeight);
	node->getAttr("nMips", m_nMips);
	int format;
	node->getAttr("format", format);
	m_format = ETEX_Format(format);
	node->getAttr("bIsCubemap", m_bIsCubemap);

	const char * dccstr = NULL; 
	node->getAttr("dccFilename",&dccstr);
	
	if( dccstr )
		m_strDccFilename = dccstr;

	bool bIsFav = false;
	node->getAttr("fav", bIsFav);
	SetFlag( eAssetFlags_Favourite, bIsFav );
	
	if(0 == m_nMips)
	{
		SetFlag(eAssetFlags_HasErrors, true);
	}

	if(0 >= m_nTextureWidth || 0 >= m_nTextureHeight)
	{
		SetFlag(eAssetFlags_Invalid, true);
	}

	SetFlag(IAssetDisplay::eAssetFlags_CachedFieldsInfo, true);
}